/**
 * XmlEditorUtils.java
 * created by wesley at 2008-6-15
 */
package net.sf.filelink.utl;

import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.Region;
import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
import org.eclipse.wst.sse.core.utils.StringUtils;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMAttr;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMElement;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode;
import org.w3c.dom.Attr;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;

/**
 * XmlEditorUtils
 * 
 * @author wesley
 */
public class XmlEditorUtils {

    /**
     * Returns the attribute from given node at specified offset.
     */
    public static final Attr getAttrByOffset(Node node, int offset) {
        if ((node instanceof IndexedRegion) && ((IndexedRegion) node).contains(offset) && (node.hasAttributes())) {
            NamedNodeMap attrs = node.getAttributes();
            // go through each attribute in node and if attribute contains
            // offset, return that attribute
            for (int i = 0; i < attrs.getLength(); ++i) {
                // assumption that if parent node is of type IndexedRegion,
                // then its attributes will also be of type IndexedRegion
                IndexedRegion attRegion = (IndexedRegion) attrs.item(i);
                if (attRegion.contains(offset)) {
                    return (Attr) attrs.item(i);
                }
            }
        }
        return null;
    }

    /**
     * Returns the node from given document at specified offset.
     * 
     * @param offset
     *            the offset with given document
     * @return Node either element, doctype, text, or null
     */
    public static final Node getNodeByOffset(IDocument document, int offset) {
        // get the node at offset (returns either: element, doctype, text)
        IndexedRegion inode = null;
        IStructuredModel sModel = null;
        try {
            sModel = org.eclipse.wst.sse.core.StructuredModelManager.getModelManager()
                    .getExistingModelForRead(document);
            if (sModel == null && document instanceof IStructuredDocument) {
                sModel = org.eclipse.wst.sse.core.StructuredModelManager.getModelManager().getModelForRead(
                        (IStructuredDocument) document);
            }
            inode = sModel.getIndexedRegion(offset);
            if (inode == null) {
                inode = sModel.getIndexedRegion(offset - 1);
            }
        } finally {
            if (sModel != null) {
                sModel.releaseFromRead();
            }
        }

        if (inode instanceof Node) {
            Attr attribute = getAttrByOffset((Node) inode, offset);
            if (attribute != null) {
                return attribute;
            }
            return (Node) inode;
        }
        return null;
    }

    /**
     * Returns the text region of given node.
     */
    public static final IRegion getHyperlinkRegion(Node node) {
        if (node != null) {
            switch (node.getNodeType()) {
                case Node.DOCUMENT_TYPE_NODE:
                case Node.TEXT_NODE:
                    IDOMNode docNode = (IDOMNode) node;
                    return new Region(docNode.getStartOffset(), docNode.getEndOffset() - docNode.getStartOffset());

                case Node.ELEMENT_NODE:
                    IDOMElement element = (IDOMElement) node;
                    int endOffset;
                    if (element.hasEndTag() && element.isClosed()) {
                        endOffset = element.getStartEndOffset();
                    } else {
                        endOffset = element.getEndOffset();
                    }
                    return new Region(element.getStartOffset(), endOffset - element.getStartOffset());

                case Node.ATTRIBUTE_NODE:
                    IDOMAttr att = (IDOMAttr) node;
                    // do not include quotes in attribute value region
                    int regOffset = att.getValueRegionStartOffset();
                    int regLength = att.getValueRegionText().length();
                    String attValue = att.getValueRegionText();
                    if (StringUtils.isQuoted(attValue)) {
                        regOffset += 1;
                        regLength = regLength - 2;
                    }
                    return new Region(regOffset, regLength);
            }
        }
        return null;
    }

}
